<!DOCTYPE html>
<!-- saved from url=(0047)http://littleb.tc/slides/2012/everyone/git.html -->
<html class="js flexbox canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms no-csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths ready"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <title>寫給大家的 Git 教學 2nd Impact - 小鎮遊跡</title>
    <meta name="author" content="Hsiao-Ting Yu (Littlebtc)">

    
    <meta name="description" content="寫給大家的 Git 教學 第三版，2012 Littlebtc (Hsiao-Ting Yu) 改寫自 Scott Chacon 的「Pro Git」電子書。 Part 1 Why Git 為什麼要版本控制？為什麼要用 Git？ 這是程式設計師的日常 當你寫了一個新東西，卻發生了以下的悲劇&amp;# …">
    
    <meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1">

    <link href="http://littleb.tc/atom.xml" rel="alternate" title="小鎮遊跡" type="application/atom+xml">
    <link rel="canonical" href="">
    <link href="http://littleb.tc/favicon.png" rel="shortcut icon">
    
    <!--[if lt IE 9]><script src="/javascripts/html5.js"></script><![endif]-->
    <script type="text/javascript" async="" src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/ga.js"></script><script src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/jquery.min.js"></script>
    
<script src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/modernizr.custom.js"></script>
<script src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/deck.core.js"></script>
<script src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/deck.hash.js"></script>
<link rel="stylesheet" href="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/deck.core.css" type="text/css">
<link rel="stylesheet" href="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/deck.hash.css" type="text/css">
<link rel="stylesheet" href="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/littlebtc.css" type="text/css">
<link rel="stylesheet" href="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/horizontal-slide.css" type="text/css">

<script src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/jquery.anystretch.min.js"></script><style type="text/css"></style>

    
  <script type="text/javascript">
    var _gaq = _gaq || [];
    _gaq.push(['_setAccount', 'UA-29719621-1']);
    _gaq.push(['_trackPageview']);

    (function() {
      var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
      ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
      var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
    })();
  </script>


</head>


<body class="deck-container on-slide-0 on-slide-slide-0">
  <section class="slide deck-current" id="slide-0">
  <div class="vcenter">
    <h1>寫給大家的 Git 教學</h1>
    <p>第三版，2012</p>
    <p>Littlebtc (Hsiao-Ting Yu)</p>
    <p style="font-weight: normal; font-size: 80%;">改寫自 Scott Chacon 的「<a href="http://progit.org/book/" target="_blank">Pro Git</a>」電子書。<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/"><img alt="Creative Commons License" style="border-width: 0" src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/80x15.png"></a></p>
  </div>
</section>
<section class="slide deck-next" id="slide-1">
  <div class="vcenter">
    <p>Part 1</p>
    <h2>Why Git</h2>
    <p>為什麼要版本控制？為什麼要用 Git？</p>
  </div>
</section>
<section class="slide deck-after" id="slide-2">
  <h2>這是程式設計師的日常</h2>
  <p>當你寫了一個新東西，卻發生了以下的悲劇…</p>
  <ul>
    <li>忘了自己改了哪些地方</li>
    <li>把舊的東西改壞了，改不回來</li>
    <li>拿去跟別人的成果合併，卻兜不起來</li>
  </ul>
  <p>→有沒有辦法解決？</p>
</section>
<section class="slide deck-after" id="slide-3">
  <h2>有！「版本控制系統」</h2>
  <p>當你使用版本控制系統時：</p>
  <ul>
    <li><span style="color: #AAAAAA;">忘了自己改了哪些地方</span>→每次的更動都會被記錄下來</li>
    <li><span style="color: #AAAAAA;">把舊的東西改壞了，改不回來</span>→可以隨時退回到過去的版本</li>
    <li><span style="color: #AAAAAA;">拿去跟別人的成果合併，卻兜不起來</span>→提供機制處理多人協作的衝突</li>
  </ul>
  <p>→「凡走過必留下痕跡」、「三個臭皮匠，勝過一個諸葛亮」，這就是版本控制的精神。</p>
</section>
<section class="slide deck-after" id="slide-4">
  <h2>版本控制系統的演進</h2>
  <ul>
    <li>單機式（rcs）</li>
    <li>中心式（CVS、Subversion）</li>
    <li>分散式（Git、Mercurial、Bazaar）</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-5">
  <h2>版本控制系統的演進</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/local-vcs.png" style="float: right;">
  <h3>單機式</h3>
  <p>為了達成「凡走過必留下痕跡」：</p>
  <ul>
    <li>本機中建立一個資料庫</li>
    <li>記下每個檔案的版本變更</li>
  </ul>
  <p><code>rcs</code> (1982) 屬於此類（至今還有人使用！）</p>
  <p>→問題：兩人以上協作時，如何同步每個人的版本資料庫？</p>
</section>
<section class="slide deck-after" id="slide-6">
  <h2>版本控制系統的演進</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/centralized-vcs.png" style="float: right;">
  <h3>中心式</h3>
  <p>為了讓三個臭皮匠可以一起作業：</p>
  <ul>
    <li>版本資料庫放在中心統一控管</li>
    <li>每個人從中心取出（Checkout）東西</li>
    <li>修改完後將內容提交（Commit）回中心</li>
  </ul>
  <p>早期盛行<code>CVS</code> (1990)、現在主流為 <code>Subversion</code> (2000)</p>
  <p>→中心一旦故障，大家的作業都會出問題，該怎麼辦？</p>
</section>
<section class="slide deck-after" id="slide-7">
  <h2>版本控制系統的演進</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/distributed-vcs.png" style="width: 300px; float: right;">
  <h3>分散式</h3>
  <p>…何不讓每個人都有一份完整的資料庫？</p>
  <ul>
    <li>「大家都能獨立工作」</li>
    <li>「Server 爛了？不要緊，拿到一份好的資料庫灌回去就可以全部復原！」</li>
  </ul>
  <p><code>Git</code> (2005) 是主流、<code>Mercurial</code> (2005) 跟 <code>Bazaar</code> (2005) 也很盛行</p>
  <p>→又來了：怎麼解決每個人之間的同步問題？<br>引進非線性的開發模式！</p>
</section>
<section class="slide deck-after" id="slide-8">
  <h2>Git 的歷史</h2>
  <p>簡言之：為了解決 Liunx 核心的開發問題，所以有了 Git</p>
  <ul>
    <li>過去，Linux 核心開發沒有使用版本控制系統（主因是 Linus Torvalds 不喜歡中心式的系統）。</li>
    <li>2002 年，開始改用專有的分散式版本控制系統 BitKeeper，讓自由軟體/開源社群不滿<small>（自由的東西用非自由的開發工具，成何體統？）</small>。</li>
    <li>2005 年 BitKeeper 跟 Linux 開發團隊鬧翻，不再免費供應系統。Git 的開發自此開始。</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-9">
  <h2>Git 的強項</h2>
  <ul>
    <li>快</li>
    <li>簡單</li>
    <li>非線性開發<br>（分支、合併、不受單一主線拘束）</li>
    <li>完全分散式</li>
    <li>處理超大資料的能力<br>（Linux 核心的程式碼超過一千萬行！）
    </li>
  </ul>
</section>
<section class="slide deck-after" id="slide-10">
  <h2>這些專案都在用 Git</h2>
  <ul>
    <li>Linux Kernel</li>
    <li>Android</li>
    <li>GNOME</li>
    <li>KDE</li>
    <li>PHP <small>(Since 2012)</small></li>
    <li>Ruby on Rails</li>
    <li>django</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-11">
  <div class="vcenter">
    <p>Part 2</p>
    <h2>Setup Git</h2>
    <p>設定屬於你的 Git 開發環境。</p>
  </div>
</section>
<section class="slide deck-after" id="slide-12">
  <h2>設定 Git</h2>
  <p>Git 的操作大部分都是透過命令列，但也有許多的圖形介面工具。</p>
  <p>無論如何，以下的設定都是必要的：</p>
  <ul>
    <li>安裝 Git 軟體<br>這不用說了吧 XD</li>
    <li>產生一個 SSH 金鑰<br>為什麼？因為 Git 可以利用 SSH 進行使用者驗證</li>
    <li>調整 Git 的組態<br>設定姓名和電子郵件地址</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-13">
  <h2>有懶人包幹嘛不用？</h2>
  <p>GitHub 他們寫了三種平台下安裝 Git 的懶人包，以下提供連結：</p>
  <ul>
    <li>Windows：<a href="http://help.github.com/win-set-up-git/" target="_blank">http://help.github.com/win-set-up-git/</a></li>
    <li>MacOS X：<a href="http://help.github.com/mac-set-up-git/" target="_blank">http://help.github.com/mac-set-up-git/</a></li>
    <li>Linux：<a href="http://help.github.com/linux-set-up-git/" target="_blank">http://help.github.com/linux-set-up-git/</a></li>
  </ul>
  <p>「Add your SSH key to GitHub」這個步驟是 GitHub 的設定，可以跳過去 :)</p>
</section>
<section class="slide deck-after" id="slide-14">
  <h2>Windows</h2>
  <p>照上面懶人包安裝後，您會把 Git for Windows 安裝好。</p>
  <p>Git for Windows 有兩種不同的使用方式：</p>
  <ul>
    <li>Git Bash：一個 Bash Shell，你可以在裡頭以命令列方式使用 Git。</li>
    <li>Git GUI：一個簡單的圖形使用介面。</li>
  </ul>
  <h3>圖形介面工具推薦</h3>
  <ul>
    <li><a href="http://code.google.com/p/tortoisegit/" target="_blank">TortoiseGit</a><br>免費，在檔案總管下使用，需先安裝 Git for Windows</li>
    <li><a href="http://code.google.com/p/gitextensions/" target="_blank">Git Extensions</a>（免費）</li>
    <li><a href="http://www.syntevo.com/smartgit/index.html" target="_blank">SmartGit</a>（跨平台，非商業使用免費）</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-15">
  <h2>MacOS X</h2>
  <p>安裝完後在「工具程式」→「終端機」以命令列使用 Git。</p>
  <p>（Mac 下另外建議您使用 <a href="http://www.iterm2.com/" target="_blank">iTerm 2</a> 取代內建的終端機程式）
  </p><h3>圖形介面工具推薦</h3>
  <ul>
    <li><a href="http://www.sourcetreeapp.com/" target="_blank">SourceTree</a><br>免費，也支援 Mercurial 和 SVN</li>
    <li><a href="http://www.gitx.org/" target="_blank">GitX</a><br>免費，有只能瀏覽歷史和 Commit 的原始版，和後人改過的全方位版</li>
    <li><a href="http://mac.github.com/" target="_blank">GitHub for Mac</a>（免費）</li>
    <li><a href="http://www.git-tower.com/" target="_blank">Tower</a>（USD $59）</li>
    <li>Xcode 4 內建 Git 支援（需要 Lion 以上）</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-16">
  <h2>Linux</h2>
  <p>在終端機中以命令列使用 Git。</p>
  <h3>圖形介面工具推薦（GNOME）</h3>
  <ul>
    <li><a href="http://www.rabbitvcs.org/" target="_blank">RabbitVCS</a><br>免費，Nautilus 檔案管理員下操作<br>Ubuntu 12.04 內附的版本沒有 Git 支援，請參照官網設定 PPA 安裝</li>
    <li><a href="http://git.gnome.org/browse/gitg/">gitg</a><br>GitX 的 GNOME fork</li>
    <li><a href="http://live.gnome.org/giggle">giggle</a></li>
  </ul>
</section>

<section class="slide deck-after" id="slide-17">
  <div class="vcenter">
    <p>Part 3</p>
    <h2>Git Basics</h2>
    <p>介紹如何建立一個 Git 的 Repository，並在其中新增新的 Commit。</p>
  </div>
</section>
<section class="slide deck-after" id="slide-18">
  <h2>所有事都能單獨在本機進行！</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/git-local.png" style="float: left; margin-right: 50px;">
  <ul>
    <li>你不只有一份完整的資料庫……</li>
    <li>閱讀版本歷史、提交變更這些動作都可以在本機進行</li>
    <li>不需要網路連線也能單獨工作！</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-19">
  <h2>建立 Repository</h2>
  <ul>
    <li>「Repository」（倉庫、套件庫）是 Git 對版本資料庫的稱呼。</li>
    <li>下達 <code>git init</code> 後，會在該目錄裡建立一個 Repository。</li>
    <li>Repository 所需的檔案會放置在 <code>.git</code> 目錄之中。<br>（因此，千萬不要誤砍了！）</li>
  </ul>
    <p>為了方便解說，以下操作全部都在命令列下進行。<br>若使用圖形介面，請試著找出相同的使用方法吧！</p>
</section>
<section class="slide deck-after" id="slide-20">
  <h2>建立 Repository</h2>
  <p><strong>範例：</strong>建立一個空的目錄叫 playground，在那裡建立 Git Repository：</p>
  <ul>
    <li><code>mkdir playground</code></li>
    <li><code>cd playground</code></li>
    <li><code>git init</code></li>
  </ul>
  <p><strong>Tips：</strong> 其實也可以用 <code>git init playground</code> 一次建空目錄和 Repository</p>
</section>
<section class="slide deck-after" id="slide-21">
  <h2>基本觀念：Commit</h2>
  <ul>
    <li>版本控制就是<strong>把當時 Repository 內所有檔案的現狀</strong>作記錄與控制。</li>
    <li>而在 Git 之中，每一次的「現狀紀錄」稱之為 <strong>Commit</strong>。</li>
    <li>Commit 中也包含作者、時間、紀錄資訊（Log）、前後對應 Commit 等資訊，方便追蹤管理。</li>
  </ul>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/commit-example.png">
</section>
<section class="slide deck-after" id="slide-22">
  <h2>基本觀念：Staging</h2>
  <ul>
    <li>在您的目錄（稱為 <strong>Working Directory</strong>，工作中目錄）之中作業。</li>
    <li><strong>唯有放進 Staging Area</strong>（暫存區）<strong>裡的更動</strong>會被 Commit。</li>
    <li>為什麼？(1) 多項目同時作業下，可以分開切成多個 Commit，方便管理。 (2) 不用擔心未完成或暫存的東西影響到 Commit。</li>
    <li>就算是同一檔案，也可以只有某幾行加入 Staging 中！</li>
  </ul>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/staging.png">
  <p style="font-size: 10px;">Diagram from <a href="http://whygitisbetterthanx.com/" target="_blank">Why Git is Better than X</a>, MIT License</p>
</section>
<section class="slide deck-after" id="slide-23">
  <h2>基本觀念：檔案狀態</h2>
  <ul>
    <li>untracked：沒有納入版本控制範圍內的檔案。</li>
    <li>unmodified / modified：沒有變動 / 有變動還沒有 Staging 的檔案。</li>
    <li>staged：Staging Area 中的檔案。</li>
  </ul>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/file-status.png">
</section>
<section class="slide deck-after" id="slide-24">
  <h2>相關的指令（新增）</h2>
  <ul>
    <li><code>git add &lt;paths&gt;</code>：把檔案新增或變動加入 Staging Area。</li>
    <li><code>git status</code>：檢視檔案狀態。</li>
    <li><code>git commit</code>：開啟編輯器，確認變動並輸入訊息後送出 Commit。<br><small>指令中加 <code>-m 'Commit 訊息'</code> 就會直接 Commit</small></li>
    <li><code>git commit --amend</code>：「更動」上一次的 Commit<br>將上次 Commit 跟新的更動合併為新的 Commit。</li>
  </ul>
  <p><strong>小秘訣：</strong><code>git commit -a</code> 會將所有變更都加入 Staging Area 之後 Commit。（但還是建議您善用 Staging :P）</p>
</section>
<section class="slide deck-after" id="slide-25">
  <h2>相關的指令（移除或補救）</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/file-status.png" style="float: right; width: 450px;">
  <ul>
    <li><code>git reset HEAD &lt;paths&gt;</code>：把檔案中 Staging 的部份注銷掉。</li>
    <li><code>git rm &lt;paths&gt;</code>：將檔案從版本控制中移除後，刪除檔案。<br><small>加 <code>-r</code> 採遞迴方式，<code>--cached</code> 則不刪除原來檔案。</small></li>
    <li><code>git checkout -- &lt;paths&gt;</code>：恢復檔案為未更動的狀態（對 Staging 的部份無效）。</li>
  </ul>
</section>

<section class="slide deck-after" id="slide-26">
  <h2>實例：第一個 Commit</h2>
  <p>用 <code>touch README</code> 建立一個空的 README 檔案後：</p>
  <p><code>git add README</code><br>
  <code>git commit -m 'First commit'</code></p>
  <p>這樣就會建立一個 Commit，將 README 納入版本控制之中。</p>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/first-commit.png">
</section>
<section class="slide deck-after" id="slide-27">
  <h2>Commit 的內部結構</h2>
  <ul>
    <li>利用 <code>tree</code> 存放檔案一覽，將檔案存在 <code>blob</code> 裡。</li>
    <li>所有的資料都以 SHA1 checksum 標記，防止損毀或中途更改。</li>
    <li>Git 針對每個檔案版本獨立紀錄，而非只紀錄之間的差異，更具便利性。</li>
  </ul>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/commit-data.png" style="width: 450px;">
</section>
<section class="slide deck-after" id="slide-28">
  <h2>p.s.「我不要這個 Commit！」</h2>
  <p>有兩種方法：</p>
  <ul>
    <li>如果你還沒 Push 給別人（後述），您可以直接把整個樹退回到前次 Commit 時的狀態：<br><code>git reset HEAD^</code></li>
    <li>如果你已經 Push 給別人，別人已經有您新的 Commit 了，因此上述的救法無效。您可以用 Revert，製造一個跟目前 Commit 差異完全相反過來的 Commit：<br><code>git revert HEAD</code></li>
  </ul>
</section>

<section class="slide deck-after" id="slide-29">
  <div class="vcenter">
    <p>Part 4</p>
    <h2>Tree Management</h2>
    <p>介紹 Git 樹狀的歷史管理方式，Branch 和 Tag 的使用，以及 Merge 和 Rebase 的概念。</p>
  </div>
</section>
<section class="slide deck-after" id="slide-30">
  <h2>當 Commit 超過一個時…</h2>
  <ul>
    <li>Git 會用樹狀的方式紀錄 Commit 的繼承關係。</li>
    <li>其實踐方式就像是 Linked List，在每個 Commit 中記下他的 Parent。</li>
    <li>而 Git 允許 Commit 的分岔和合併，因此可以進行複雜的樹狀操作。</li>
  </ul>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/multiple-commits-data.png">
</section>
<section class="slide deck-after" id="slide-31">
  <h2>Tag 與 Branch</h2>
  <p>在 Git 中，Tag 和 Branch <strong>是指向某一特定 Commit 的指標</strong>：</p>
  <ul>
    <li>
      Branch 是<strong>會動的</strong>：使用中的 Branch，指向的位置會在新 Commit 出現後自動移動。每一個 Repository 都會有一個預設的 Branch 叫 <code>master</code>。
      <ul>
        <li>Git 會使用 <code>HEAD</code> 指標去紀錄目前所使用的 Branch。</li>
        <li>可以隨時新增、切換和刪除 Branch（因為它只是指標！）</li>
        <li>分岔的 Branch 可以透過 Merge 和 Rebase 去合併。</li>
      </ul>
    </li>
    <li>Tag 是<strong>不會動的</strong>：對某一個特定的 Commit 加上標記，指向位置不會隨新 Commit 出現更動。用於標記釋出版本或里程碑。</li>
</ul></section>
<section class="slide deck-after" id="slide-32">
  <h2>實例一：我在哪裡？</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-sample.png">
  <p>一個擁有三個 Commit 的 Repository。這時只有一個預設的 <code>master</code> Branch。</p>
</section>
<section class="slide deck-after" id="slide-33">
  <h2>實例一：我在哪裡？</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex1-1.png">
  <p><code>git branch testing</code><br>建立一個新的 Branch 叫 <code>testing</code>，指向目前所在的 Commit。</p>
</section>
<section class="slide deck-after" id="slide-34">
  <h2>實例一：我在哪裡？</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex1-2.png">
  <p><code>git checkout testing</code><br>切換到 <code>testing</code> Branch：<code>HEAD</code> 會變成指向 <code>testing</code>，然後切換到該 Branch 指向的 Commit。</p>
  <p>小秘訣：<code>git checkout -b testing</code> 可以一次做完開新 Branch + 切換</p>
</section>
<section class="slide deck-after" id="slide-35">
  <h2>實例一：我在哪裡？</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex1-3.png">
  <p>如果在這時新增新的 Commit，會移動 <code>testing</code> Branch 指向的位置，而不會移動 <code>master</code>。<br>（也就是只有 <code>HEAD</code> 指到的 Branch 會動）</p>
</section>
<section class="slide deck-after" id="slide-36">
  <h2>實例一：我在哪裡？</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex1-4.png">
  <p>用 <code>git checkout master</code> 切換到 <code>master</code> 和其對應的 Commit</p>
</section>
<section class="slide deck-after" id="slide-37">
  <h2>實例一：我在哪裡？</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex1-5.png">
  <p>在 master 下進行 Commit 後，由於同一個 Commit 有兩個分支，產生分岔！</p>
</section>
<section class="slide deck-after" id="slide-38">
  <h2>Branch 岔開之後呢？</h2>
  <ul>
    <li>可以透過 Merge 「合起來」
      <ul>
        <li>假如 Merge 的目標只是這個 Branch 往後推的幾個 Commit，只要把 Branch 往後推就好，這樣叫 Fast-forward merge。</li>
        <li>無法使用 Fast forward 的狀況，Git 會嘗試用策略去解決並合併分支。簡單、問題較少，但合併次數一多，會讓樹看起來很亂 :P。</li>
      </ul>
      
    </li>
    <li>或透過 Rebase 「接上去」<br>複雜，容易產生衍生問題，但結果較漂亮<br><strong>已經 Push 給別人的東西千萬不要用 Rebase！</strong></li>
  </ul>
</section>
<section class="slide deck-after" id="slide-39">
  <h2>實例二：Merge</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex2-1.png" style="float: left; margin-right: 50px;">
  <ul>
    <li><code>iss53</code> 用來修正 Issue 53，但還沒寫完</li>
    <li><code>hotfix</code> 是為了解決現在 <code>master</code> 上迫切的問題<br>已經寫好了，我想合併進 <code>master</code></li>
  </ul>
</section>
<section class="slide deck-after" id="slide-40">
  <h2>實例二：Merge</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex2-2.png" style="float: left; margin-right: 50px;">
  <p><code>git checkout master</code><br><code>git merge hotfix</code></p>
  <ul>
    <li>切回 <code>master</code> 後，「把 <code>hotfix</code> 裡的變更合併進來」</li>
    <li>因為合併的對象是後面幾個 Commit，可以透過 Fast Forward 把 Branch 往後推解決！ :)</li>
    <li>完成後，我就可以把 <code>git branch -d hotfix</code> 把不需要的 Branch 刪掉了</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-41">
  <h2>實例二：Merge</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex2-3.png" style="float: left; margin-right: 50px;">
  <ul>
    <li>這時我把 <code>iss53</code> 寫好了，要 Merge 回去</li>
    <li><code>git checkout master</code><br><code>git merge iss53</code><p></p>
    </li><li>但，這種 Merge 沒辦法透過 Fast Forward 解決…不過所幸它們有共同的祖先，可以做 3-way Merge。</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-42">
  <h2>實例二：Merge</h2>
  <ul>
    <li>3-way Merge 的實作：比較祖先和合併對象間（C2 與 C4 間和 C2 與 C5 間）的變動，將這些變動合併起來</li>
  </ul>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex2-4.png" style="width: 400px;">
</section>
<section class="slide deck-after" id="slide-43">
  <h2>實例二：Merge</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex2-5.png" style="float: left; margin-right: 50px;">
  <p>合併的結果會產生新的 Commit C6，C4 和 C5 是其共同的 parent。至此完成分支的合併。</p>
</section>
<section class="slide deck-after" id="slide-44">
  <h2>實例三：Rebase</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex3-1.png" style="float: left; margin-right: 50px;">
  <p>我想把 <code>experiment</code> 的變更丟進去 <code>master</code>，但又不想用 Merge 製造額外的分支，要怎麼辦？</p>
</section>
<section class="slide deck-after" id="slide-45">
  <h2>實例三：Rebase</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex3-2.png" style="float: left; margin-right: 50px;">
  <p><code>git checkout experiment</code><br><code>git rebase master</code></p>
  <p>把 C3 的變更 「蓋到 <code>master</code> 裡面去」</p>
  <p>接下來就只要切回 <code>master</code>，把 <code>experiment</code> 合併好就搞定了 :)</p>
  <h5>為什麼「已經 Push 給別人的東西不能 Rebase」？</h5>
  <p>Rebase 過的東西跟還沒 Rebase 時的東西可能混在一起，整個亂掉…</p>
</section>
<section class="slide deck-after" id="slide-46">
  <h2>實例四：複雜一點的 Rebase</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex4-1.png" style="float: left; margin-right: 50px;">
  <p>因為 <code>server</code> 寫得爛爛的，我想把 <code>client</code> 的變更蓋到 <code>master</code> 上而非 <code>server</code> 上…</p>
</section>
<section class="slide deck-after" id="slide-47">
  <h2>實例四：複雜一點的 Rebase</h2>
  <h5>辦得到！</h5>
  <p><code>git rebase --onto master server client</code></p>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/branch-ex4-2.png" style="float: left; margin-right: 50px;">
  <ul>
    <li>檢查 <code>client</code>，由 <code>client</code> 和 <code>server</code> 的共同祖先和其比較看作了哪些變動，把那些變更重新在 <code>master</code> 上面作過一遍</li>
    <li>一樣，剩下的就是 Merge 而已了</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-48">
  <h2>Conflict!!</h2>
  <p>當在 3-Way Merge 時發現雙方更動同樣一塊內容時，就會發生衝突。</p>
  <ul>
    <li>Git 遇到衝突時會停下來，把未衝突到的地方給 Staging 起來，把衝突到的地方標示起來。</li>
    <li>這時可以透過 <code>git status</code> 找出衝突的檔案（unmerged）</li>
    <li>打開那些檔案，手動把衝突之處修正後，重新 Commit 就能解決衝突。</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-49">
  <h2>Branch 的其他指令</h2>
  <ul>
    <li><code>git branch</code>：顯示目前所有的 Branch，會用星號標示目前所在者<br>以 <code>git branch -v</code>同時顯示各 Branch 所在的 Commit</li>
    <li><code>git branch --merged</code>：顯示已合併到目前 Branch 的其他 Branch<br>這樣可以把沒用到的 Branch 找出來刪掉…</li>
    <li><code>git branch --no-merged</code>：顯示未合併到目前 Branch 的其他 Branch</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-50">
  <h2>Tag</h2>
  <ul>
    <li><code>git tag v1.0</code>：把現在的 Commit 加上名為 <code>v1.0</code> 的 Tag</li>
    <li><code>git tag v1.0 abcde</code>：對代碼為 <code>abcde</code> 的 Commit 加上 <code>v1.0</code> Tag</li> 
    <li><code>git tag -d v1.0</code>：刪除名為 <code>v1.0</code> 的 Tag</li>
  </ul>
  <p>Git 另有功能較為強大的 Annotated Tags，可以在 Tag 上加注作者資訊和認證機制。</p>
  <p>p.s. Push 時，用 <code>git push --tags</code> 才會把 Tag 給一起 Push 上去</p>
</section>
<section class="slide deck-after" id="slide-51">
  <h2>推薦的分支方式</h2>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/git-flow.png" style="float: left; margin-right: 50px; width: 360px;">
  <ul>
    <li>「每個 Branch 都分開處理一項事情」</li>
    <li>將測試中和穩定的版本分開為不同的 Branch</li>
    <li>將需要很長時間才能完成的獨立項目拆成 Branch</li>
    <li><a href="http://nvie.com/posts/a-successful-git-branching-model/" target="_blank">A successful Git branching model</a> 一文中歸納了 Git 用於軟體開發時的分支最佳實踐。有人基於這個概念設計了 <a href="http://ihower.tw/blog/archives/5140" target="_blank">git-flow 工具</a>。</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-52">
  <h2>p.s. 「我想要乾淨的樹」</h2>
  <p>在進行 Branch 切換，或著 Rebase 的時候，會要求您的 Working Directory 必須是乾淨（沒有變動）的，但改到一半的東西又放棄不了？該怎麼辦？</p>
  <p>→使用 Stashing 把變動放到外太空</p>
  <ul>
    <li><code>git stash</code>：把目前的變更（包含 Staging 的部份）放進新的 Stash<br>Stash 可以有很多個，會以堆疊的方式儲存（想成放進桶子裡）</li>
    <li><code>git stash apply</code>：把最新的 Stash 中的變更取出。</li>
    <li><code>git stash list</code>：檢視目前的 Stash 堆疊。</li>
  </ul>
</section>

<section class="slide deck-after" id="slide-53">
  <div class="vcenter">
    <p>Part 5</p>
    <h2>Remote + Collaboration</h2>
    <p>介紹透過 Remote 連到其他的 Git 遠端，並示範 Git 協作的具體做法。</p>
  </div>
</section>
<section class="slide deck-after" id="slide-54">
  <h2>Remote（遠端）</h2>
  <ul>
    <li>讓您的 Repository 同步到其他的伺服器中</li>
    <li>當多人協作時，需要藉此整合大家的東西</li>
    <li>Git <strong>允許附加多個遠端</strong>，因此可以做複雜的同步工作</li>
  </ul>
  <p>以下將先介紹多人協作的架構方式與 Git 伺服器的選擇後，介紹 Remote 的使用方式。</p>
</section>
<section class="slide deck-after" id="slide-55">
  <h2>多人協作：單一中心式</h2>
  <ul>
    <li>類似中心式版本控制系統，所有人跟一個共同的遠端 Repository 進行同步。</li>
    <li>但跟中心式相比，遇到衝突時，可以透過 Merge 和 Rebase 來解決，不會有卡住的情形。</li>
    <li>適合人數跟規模較小的情況。</li>
  </ul>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/centralized-workflow.png">
</section>
<section class="slide deck-after" id="slide-56">
  <h2>多人協作：整合管理員式</h2>
  <ul>
    <li>有一個「正統」（Blessed）的 Repository 來存放最終版本</li>
    <li>每個開發者把自己的貢獻整理好後，傳到自己公開的 Repository。</li>
    <li>由整合管理員決定是否每個開發者改的東西合併到正統裡。</li>
  </ul>
  <p>這種模式就是 GitHub 或 BitBucket 等系統的 Fork 與 Pull Request 機制：開發者可以把正統版 Fork 下來改，改好後送 Pull Request 給整合管理員。</p>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/im-workflow.png">
</section>
<section class="slide deck-after" id="slide-57">
  <h2>多人協作：司令官與附手</h2>
  <ul>
    <li>當專案規模超大時，就需要兩層甚至以上的組織。</li>
    <li>每個開發者的成果由附手彙整後，交由司令官作最後整合。</li>
  </ul>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/d-and-l-workflow.png">
</section>
<section class="slide deck-after" id="slide-58">
  <h2>Git 遠端的選擇</h2>
  <ul>
    <li>網路上的現有服務：GitHub、Bitbucket、…</li>
    <li>自行架設：Gitolite、Gitosis、…</li>
    <li>檔案系統（NFS、隨身碟…）</li>
    <li style="font-size: 70%;"><del>Dropbox</del>（雖然可以，但就失去 Git 的價值了）</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-59">
  <h2><a href="https://github.com/" target="_blank">GitHub</a></h2>
  <ul>
    <li>主打社群網路功能的 Git 托管服務</li>
    <li>具有強大的群組、Fork / Pull Request 和其他附加功能，方便多人協作與溝通</li>
    <li>公開 / Open Source 的專案免費，私密專案則要購買收費服務</li>
  </ul>
  <p><a href="https://github.com/" target="_blank">https://github.com/</a></p>
</section>
<section class="slide deck-after" id="slide-60">
  <h2><a href="https://bitbucket.org/" target="_blank">Bitbucket</a></h2>
  <ul>
    <li>早期只支援 Mercurial，近期開始支援 Git</li>
    <li>類似 GitHub，也有 Fork / Pull Request 的機制</li>
    <li>公開專案和<strong>五人以下協作的私密專案</strong>免費，另有收費服務</li>
  </ul>
  <p><a href="https://bitbucket.org/" target="_blank">https://bitbucket.org/</a></p>
</section>
<section class="slide deck-after" id="slide-61">
  <h2>自行架設</h2>
  <p>假如您有一個自己的 Linux 主機，您可以使用 Gitosis 或 Gitolite 架設一個自己的 Remote Server，請參考 Pro Git 上的說明：</p>
  <ul>
    <li><a href="http://progit.org/book/ch4-8.html" target="_blank">Gitolite</a>（推薦）</li>
    <li><a href="http://progit.org/book/ch4-7.html" target="_blank">Gitosis</a></li>
  </ul>
  <p>或著，如果您有 NFS 或隨身碟，可以直接透過檔案系統作為 Remote。</p>
</section>
<section class="slide deck-after" id="slide-62">
  <h2>Remote Protocol</h2>
  <p>Git 提供以下四種 Remote 的協定：</p>
  <ul>
    <li>Local：以本機路徑作為 Remote 目標，適用於架設於 NFS 時。<br><code>/paths/to/remote.git</code> 或 <code>file:///opt/git/project.git</code></li>
    <li>SSH：連接網路的協定，可讀可寫。可以透過 SSH Key 進行認證。<br><code>username@server.tld:project.git</code><br>通常會使用 SSH，因為其方便且安全性佳。</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-63">
  <h2>Remote Protocol</h2>
  <p>Git 提供以下四種 Remote 的協定：</p>
  <ul>
    <li>Git：連接網路的協定，唯讀，但速度很快<br><code>git://server.tld/project.git</code></li>
    <li>HTTP(S)：連接網路的協定，可讀可寫。速度較慢，但適用於公司行號內有防火牆，只有 HTTP(S) Port 對外開放的環境<br><code>http://server.tld/project.git</code></li>
  </ul>
</section>
<section class="slide deck-after" id="slide-64">
  <h2>Cloning</h2>
  <p><code>git clone git@github.com:littlebtc/my-project.git</code></p>
  <p>這行指令會：</p>
  <ul>
    <li>把程式碼從指定的地方抓下來</li>
    <li>在抓下來的 Repository 中設定好一個叫做 <code>origin</code> 的 Remote 指向該處</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-65">
  <h2>Remote Branches</h2>
  <ul>
    <li>在 Remote 中 Repository 的狀態會被記錄下來</li>
    <li>因此 Remote 中也會有 Branch，前面會加上 Remote 的名字來作識別</li>
    <li><code>origin/master</code> 就是指 <code>origin</code> 這個 Remote 中的 <code>master</code> Branch</li>
  </ul>
  <img src="./寫給大家的 Git 教學 2nd Impact - 小鎮遊跡_files/origin-master-1.png" style="width: 400px;">
</section>
<section class="slide deck-after" id="slide-66">
  <h2>Remote 的相關指令</h2>
  <ul>
    <li><code>git fetch</code>：將 Remote 更新到最新的版本。</li>
    <li><code>git pull</code>：進行 Fetch 後，將 Remote Branch 的變動合併進本機的 Branch。</li>
    <li><code>git push</code>：將本機的 Branch 變動合併到 Remote Branch。</li>
  </ul>
</section>
<section class="slide deck-after" id="slide-67">
  <h2>實例</h2>
  <p>在未設定 Remote 的 Repository 中，新增 Remote 後 Push：</p>
  <ul>
    <li><code>git remote add origin git@github.com:littlebtc/my-project.git</code></li>
    <li><code>git push -u origin master</code></li>
  </ul>
</section>
<section class="slide deck-after" id="slide-68">
  <h2>其他 Git 的教學資源</h2>
  <ul>
    <li><a href="http://progit.org/book/" target="_blank">Pro Git</a></li>
    <li><a href="http://gitready.com/" target="_blank">Git Ready</a></li>
    <li><a href="http://ihower.tw/blog/archives/category/git" target="_blank">ihower 的 Git 心得文</a></li>
  </ul>
</section>

  <script type="text/javascript">
    $(document).ready(function() {
      $.deck('.slide');
    });
  </script>
<a href="http://littleb.tc/slides/2012/everyone/" title="Permalink to this slide" class="deck-permalink">#</a>


</body></html>